/*
 * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
 *
 * The Apache Software License, Version 1.1
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Caucho Technology (http://www.caucho.com/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "Hessian", "Resin", and "Caucho" must not be used to
 *    endorse or promote products derived from this software without prior
 *    written permission. For written permission, please contact
 *    info@caucho.com.
 *
 * 5. Products derived from this software may not be called "Resin"
 *    nor may "Resin" appear in their names without prior written
 *    permission of Caucho Technology.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Scott Ferguson
 */

package com.caucho.hessian.io;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.logging.Logger;

/**
 * Debugging input stream for Hessian requests.
 */
public class HessianDebugState implements Hessian2Constants {
	private static final Logger log = Logger.getLogger(HessianDebugState.class.getName());

	private PrintWriter _dbg;

	private State _state;
	private ArrayList<State> _stateStack = new ArrayList<State>();

	private ArrayList<ObjectDef> _objectDefList = new ArrayList<ObjectDef>();

	private ArrayList<String> _typeDefList = new ArrayList<String>();

	private int _refId;
	private boolean _isNewline = true;
	private boolean _isObject = false;
	private int _column;

	private int _depth = 0;

	/**
	 * Creates an uninitialized Hessian input stream.
	 */
	public HessianDebugState(PrintWriter dbg) {
		_dbg = dbg;

		_state = new InitialState();
	}

	public void startTop2() {
		_state = new Top2State();
	}

	public void startData1() {
		_state = new InitialState1();
	}

	public void startStreaming() {
		_state = new StreamingState(new InitialState(), false);
	}

	/**
	 * Reads a character.
	 */
	public void next(int ch) throws IOException {
		_state = _state.next(ch);
	}

	void pushStack(State state) {
		_stateStack.add(state);
	}

	State popStack() {
		return _stateStack.remove(_stateStack.size() - 1);
	}

	public void setDepth(int depth) {
		_depth = depth;
	}

	public int getDepth() {
		return _depth;
	}

	void println() {
		if (!_isNewline) {
			_dbg.println();
			_dbg.flush();
		}

		_isNewline = true;
		_column = 0;
	}

	static boolean isString(int ch) {
		switch (ch) {
		case 0x00:
		case 0x01:
		case 0x02:
		case 0x03:
		case 0x04:
		case 0x05:
		case 0x06:
		case 0x07:
		case 0x08:
		case 0x09:
		case 0x0a:
		case 0x0b:
		case 0x0c:
		case 0x0d:
		case 0x0e:
		case 0x0f:

		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1a:
		case 0x1b:
		case 0x1c:
		case 0x1d:
		case 0x1e:
		case 0x1f:

		case 0x30:
		case 0x31:
		case 0x32:
		case 0x33:

		case 'R':
		case 'S':
			return true;

		default:
			return false;
		}
	}

	static boolean isInteger(int ch) {
		switch (ch) {
		case 0x80:
		case 0x81:
		case 0x82:
		case 0x83:
		case 0x84:
		case 0x85:
		case 0x86:
		case 0x87:
		case 0x88:
		case 0x89:
		case 0x8a:
		case 0x8b:
		case 0x8c:
		case 0x8d:
		case 0x8e:
		case 0x8f:

		case 0x90:
		case 0x91:
		case 0x92:
		case 0x93:
		case 0x94:
		case 0x95:
		case 0x96:
		case 0x97:
		case 0x98:
		case 0x99:
		case 0x9a:
		case 0x9b:
		case 0x9c:
		case 0x9d:
		case 0x9e:
		case 0x9f:

		case 0xa0:
		case 0xa1:
		case 0xa2:
		case 0xa3:
		case 0xa4:
		case 0xa5:
		case 0xa6:
		case 0xa7:
		case 0xa8:
		case 0xa9:
		case 0xaa:
		case 0xab:
		case 0xac:
		case 0xad:
		case 0xae:
		case 0xaf:

		case 0xb0:
		case 0xb1:
		case 0xb2:
		case 0xb3:
		case 0xb4:
		case 0xb5:
		case 0xb6:
		case 0xb7:
		case 0xb8:
		case 0xb9:
		case 0xba:
		case 0xbb:
		case 0xbc:
		case 0xbd:
		case 0xbe:
		case 0xbf:

		case 0xc0:
		case 0xc1:
		case 0xc2:
		case 0xc3:
		case 0xc4:
		case 0xc5:
		case 0xc6:
		case 0xc7:
		case 0xc8:
		case 0xc9:
		case 0xca:
		case 0xcb:
		case 0xcc:
		case 0xcd:
		case 0xce:
		case 0xcf:

		case 0xd0:
		case 0xd1:
		case 0xd2:
		case 0xd3:
		case 0xd4:
		case 0xd5:
		case 0xd6:
		case 0xd7:

		case 'I':
			return true;

		default:
			return false;
		}
	}

	abstract class State {
		State _next;

		State() {
		}

		State(State next) {
			_next = next;
		}

		abstract State next(int ch);

		boolean isShift(Object value) {
			return false;
		}

		State shift(Object value) {
			return this;
		}

		int depth() {
			if (_next != null)
				return _next.depth();
			else
				return HessianDebugState.this.getDepth();
		}

		void printIndent(int depth) {
			if (_isNewline) {
				for (int i = _column; i < depth() + depth; i++) {
					_dbg.print(" ");
					_column++;
				}
			}
		}

		void print(String string) {
			print(0, string);
		}

		void print(int depth, String string) {
			printIndent(depth);

			_dbg.print(string);
			_isNewline = false;
			_isObject = false;

			int p = string.lastIndexOf('\n');
			if (p > 0)
				_column = string.length() - p - 1;
			else
				_column += string.length();
		}

		void println(String string) {
			println(0, string);
		}

		void println(int depth, String string) {
			printIndent(depth);

			_dbg.println(string);
			_dbg.flush();
			_isNewline = true;
			_isObject = false;
			_column = 0;
		}

		void println() {
			if (!_isNewline) {
				_dbg.println();
				_dbg.flush();
			}

			_isNewline = true;
			_isObject = false;
			_column = 0;
		}

		void printObject(String string) {
			if (_isObject)
				println();

			printIndent(0);

			_dbg.print(string);
			_dbg.flush();

			_column += string.length();

			_isNewline = false;
			_isObject = true;
		}

		protected State nextObject(int ch) {
			switch (ch) {
			case -1:
				println();
				return this;

			case 'N':
				if (isShift(null))
					return shift(null);
				else {
					printObject("null");
					return this;
				}

			case 'T':
				if (isShift(Boolean.TRUE))
					return shift(Boolean.TRUE);
				else {
					printObject("true");
					return this;
				}

			case 'F':
				if (isShift(Boolean.FALSE))
					return shift(Boolean.FALSE);
				else {
					printObject("false");
					return this;
				}

			case 0x80:
			case 0x81:
			case 0x82:
			case 0x83:
			case 0x84:
			case 0x85:
			case 0x86:
			case 0x87:
			case 0x88:
			case 0x89:
			case 0x8a:
			case 0x8b:
			case 0x8c:
			case 0x8d:
			case 0x8e:
			case 0x8f:

			case 0x90:
			case 0x91:
			case 0x92:
			case 0x93:
			case 0x94:
			case 0x95:
			case 0x96:
			case 0x97:
			case 0x98:
			case 0x99:
			case 0x9a:
			case 0x9b:
			case 0x9c:
			case 0x9d:
			case 0x9e:
			case 0x9f:

			case 0xa0:
			case 0xa1:
			case 0xa2:
			case 0xa3:
			case 0xa4:
			case 0xa5:
			case 0xa6:
			case 0xa7:
			case 0xa8:
			case 0xa9:
			case 0xaa:
			case 0xab:
			case 0xac:
			case 0xad:
			case 0xae:
			case 0xaf:

			case 0xb0:
			case 0xb1:
			case 0xb2:
			case 0xb3:
			case 0xb4:
			case 0xb5:
			case 0xb6:
			case 0xb7:
			case 0xb8:
			case 0xb9:
			case 0xba:
			case 0xbb:
			case 0xbc:
			case 0xbd:
			case 0xbe:
			case 0xbf: {
				Integer value = new Integer(ch - 0x90);

				if (isShift(value))
					return shift(value);
				else {
					printObject(value.toString());
					return this;
				}
			}

			case 0xc0:
			case 0xc1:
			case 0xc2:
			case 0xc3:
			case 0xc4:
			case 0xc5:
			case 0xc6:
			case 0xc7:
			case 0xc8:
			case 0xc9:
			case 0xca:
			case 0xcb:
			case 0xcc:
			case 0xcd:
			case 0xce:
			case 0xcf:
				return new IntegerState(this, "int", ch - 0xc8, 3);

			case 0xd0:
			case 0xd1:
			case 0xd2:
			case 0xd3:
			case 0xd4:
			case 0xd5:
			case 0xd6:
			case 0xd7:
				return new IntegerState(this, "int", ch - 0xd4, 2);

			case 'I':
				return new IntegerState(this, "int");

			case 0xd8:
			case 0xd9:
			case 0xda:
			case 0xdb:
			case 0xdc:
			case 0xdd:
			case 0xde:
			case 0xdf:
			case 0xe0:
			case 0xe1:
			case 0xe2:
			case 0xe3:
			case 0xe4:
			case 0xe5:
			case 0xe6:
			case 0xe7:
			case 0xe8:
			case 0xe9:
			case 0xea:
			case 0xeb:
			case 0xec:
			case 0xed:
			case 0xee:
			case 0xef: {
				Long value = new Long(ch - 0xe0);

				if (isShift(value))
					return shift(value);
				else {
					printObject(value.toString() + "L");
					return this;
				}
			}

			case 0xf0:
			case 0xf1:
			case 0xf2:
			case 0xf3:
			case 0xf4:
			case 0xf5:
			case 0xf6:
			case 0xf7:
			case 0xf8:
			case 0xf9:
			case 0xfa:
			case 0xfb:
			case 0xfc:
			case 0xfd:
			case 0xfe:
			case 0xff:
				return new LongState(this, "long", ch - 0xf8, 7);

			case 0x38:
			case 0x39:
			case 0x3a:
			case 0x3b:
			case 0x3c:
			case 0x3d:
			case 0x3e:
			case 0x3f:
				return new LongState(this, "long", ch - 0x3c, 6);

			case BC_LONG_INT:
				return new LongState(this, "long", 0, 4);

			case 'L':
				return new LongState(this, "long");

			case 0x5b:
			case 0x5c: {
				Double value = new Double(ch - 0x5b);

				if (isShift(value))
					return shift(value);
				else {
					printObject(value.toString());
					return this;
				}
			}

			case 0x5d:
				return new DoubleIntegerState(this, 3);

			case 0x5e:
				return new DoubleIntegerState(this, 2);

			case 0x5f:
				return new MillsState(this);

			case 'D':
				return new DoubleState(this);

			case 'Q':
				return new RefState(this);

			case BC_DATE:
				return new DateState(this);

			case BC_DATE_MINUTE:
				return new DateState(this, true);

			case 0x00: {
				String value = "\"\"";

				if (isShift(value))
					return shift(value);
				else {
					printObject(value.toString());
					return this;
				}
			}

			case 0x01:
			case 0x02:
			case 0x03:
			case 0x04:
			case 0x05:
			case 0x06:
			case 0x07:
			case 0x08:
			case 0x09:
			case 0x0a:
			case 0x0b:
			case 0x0c:
			case 0x0d:
			case 0x0e:
			case 0x0f:

			case 0x10:
			case 0x11:
			case 0x12:
			case 0x13:
			case 0x14:
			case 0x15:
			case 0x16:
			case 0x17:
			case 0x18:
			case 0x19:
			case 0x1a:
			case 0x1b:
			case 0x1c:
			case 0x1d:
			case 0x1e:
			case 0x1f:
				return new StringState(this, 'S', ch);

			case 0x30:
			case 0x31:
			case 0x32:
			case 0x33:
				return new StringState(this, 'S', ch - 0x30, true);

			case 'R':
				return new StringState(this, 'S', false);

			case 'S':
				return new StringState(this, 'S', true);

			case 0x20: {
				String value = "binary(0)";

				if (isShift(value))
					return shift(value);
				else {
					printObject(value.toString());
					return this;
				}
			}

			case 0x21:
			case 0x22:
			case 0x23:
			case 0x24:
			case 0x25:
			case 0x26:
			case 0x27:
			case 0x28:
			case 0x29:
			case 0x2a:
			case 0x2b:
			case 0x2c:
			case 0x2d:
			case 0x2e:
			case 0x2f:
				return new BinaryState(this, 'B', ch - 0x20);

			case 0x34:
			case 0x35:
			case 0x36:
			case 0x37:
				return new BinaryState(this, 'B', ch - 0x34, true);

			case 'A':
				return new BinaryState(this, 'B', false);

			case 'B':
				return new BinaryState(this, 'B', true);

			case 'M':
				return new MapState(this, _refId++);

			case 'H':
				return new MapState(this, _refId++, false);

			case BC_LIST_VARIABLE:
				return new ListState(this, _refId++, true);

			case BC_LIST_VARIABLE_UNTYPED:
				return new ListState(this, _refId++, false);

			case BC_LIST_FIXED:
				return new CompactListState(this, _refId++, true);

			case BC_LIST_FIXED_UNTYPED:
				return new CompactListState(this, _refId++, false);

			case 0x70:
			case 0x71:
			case 0x72:
			case 0x73:
			case 0x74:
			case 0x75:
			case 0x76:
			case 0x77:
				return new CompactListState(this, _refId++, true, ch - 0x70);

			case 0x78:
			case 0x79:
			case 0x7a:
			case 0x7b:
			case 0x7c:
			case 0x7d:
			case 0x7e:
			case 0x7f:
				return new CompactListState(this, _refId++, false, ch - 0x78);

			case 'C':
				return new ObjectDefState(this);

			case 0x60:
			case 0x61:
			case 0x62:
			case 0x63:
			case 0x64:
			case 0x65:
			case 0x66:
			case 0x67:
			case 0x68:
			case 0x69:
			case 0x6a:
			case 0x6b:
			case 0x6c:
			case 0x6d:
			case 0x6e:
			case 0x6f:
				return new ObjectState(this, _refId++, ch - 0x60);

			case 'O':
				return new ObjectState(this, _refId++);

			default:
				return this;
			}
		}
	}

	abstract class State1 extends State {
		State1() {
		}

		State1(State next) {
			super(next);
		}

		protected State nextObject(int ch) {
			switch (ch) {
			case -1:
				println();
				return this;

			case 'N':
				if (isShift(null))
					return shift(null);
				else {
					printObject("null");
					return this;
				}

			case 'T':
				if (isShift(Boolean.TRUE))
					return shift(Boolean.TRUE);
				else {
					printObject("true");
					return this;
				}

			case 'F':
				if (isShift(Boolean.FALSE))
					return shift(Boolean.FALSE);
				else {
					printObject("false");
					return this;
				}

			case 'I':
				return new IntegerState(this, "int");

			case 'L':
				return new LongState(this, "long");

			case 'D':
				return new DoubleState(this);

			case 'Q':
				return new RefState(this);

			case 'd':
				return new DateState(this);

			case 's':
				return new StringState(this, 'S', false);

			case 'S':
				return new StringState(this, 'S', true);

			case 'b':
			case 'A':
				return new BinaryState(this, 'B', false);

			case 'B':
				return new BinaryState(this, 'B', true);

			case 'M':
				return new MapState1(this, _refId++);

			case 'V':
				return new ListState1(this, _refId++);

			case 'R':
				return new IntegerState(new RefState1(this), "ref");

			default:
				printObject("x" + String.format("%02x", ch));
				return this;
			}
		}
	}

	class InitialState extends State {
		State next(int ch) {
			return nextObject(ch);
		}
	}

	class InitialState1 extends State1 {
		State next(int ch) {
			return nextObject(ch);
		}
	}

	class Top1State extends State1 {
		State next(int ch) {
			println();

			if (ch == 'r') {
				return new ReplyState1(this);
			} else if (ch == 'c') {
				return new CallState1(this);
			} else
				return nextObject(ch);
		}
	}

	class Top2State extends State {
		State next(int ch) {
			println();

			if (ch == 'R') {
				return new Reply2State(this);
			} else if (ch == 'F') {
				return new Fault2State(this);
			} else if (ch == 'C') {
				return new Call2State(this);
			} else if (ch == 'H') {
				return new Hessian2State(this);
			} else if (ch == 'r') {
				return new ReplyState1(this);
			} else if (ch == 'c') {
				return new CallState1(this);
			} else
				return nextObject(ch);
		}
	}

	class IntegerState extends State {
		String _typeCode;

		int _length;
		int _value;

		IntegerState(State next, String typeCode) {
			super(next);

			_typeCode = typeCode;
		}

		IntegerState(State next, String typeCode, int value, int length) {
			super(next);

			_typeCode = typeCode;

			_value = value;
			_length = length;
		}

		State next(int ch) {
			_value = 256 * _value + (ch & 0xff);

			if (++_length == 4) {
				Integer value = new Integer(_value);

				if (_next.isShift(value))
					return _next.shift(value);
				else {
					printObject(value.toString());

					return _next;
				}
			} else
				return this;
		}
	}

	class LongState extends State {
		String _typeCode;

		int _length;
		long _value;

		LongState(State next, String typeCode) {
			super(next);

			_typeCode = typeCode;
		}

		LongState(State next, String typeCode, long value, int length) {
			super(next);

			_typeCode = typeCode;

			_value = value;
			_length = length;
		}

		State next(int ch) {
			_value = 256 * _value + (ch & 0xff);

			if (++_length == 8) {
				Long value = new Long(_value);

				if (_next.isShift(value))
					return _next.shift(value);
				else {
					printObject(value.toString() + "L");

					return _next;
				}
			} else
				return this;
		}
	}

	class DoubleIntegerState extends State {
		int _length;
		int _value;
		boolean _isFirst = true;

		DoubleIntegerState(State next, int length) {
			super(next);

			_length = length;
		}

		State next(int ch) {
			if (_isFirst)
				_value = (byte) ch;
			else
				_value = 256 * _value + (ch & 0xff);

			_isFirst = false;

			if (++_length == 4) {
				Double value = new Double(_value);

				if (_next.isShift(value))
					return _next.shift(value);
				else {
					printObject(value.toString());

					return _next;
				}
			} else
				return this;
		}
	}

	class RefState extends State {
		String _typeCode;

		int _length;
		int _value;

		RefState(State next) {
			super(next);
		}

		RefState(State next, String typeCode) {
			super(next);

			_typeCode = typeCode;
		}

		RefState(State next, String typeCode, int value, int length) {
			super(next);

			_typeCode = typeCode;

			_value = value;
			_length = length;
		}

		@Override
		boolean isShift(Object o) {
			return true;
		}

		@Override
		State shift(Object o) {
			println("ref #" + o);

			return _next;
		}

		@Override
		State next(int ch) {
			return nextObject(ch);
		}
	}

	class RefState1 extends State {
		String _typeCode;

		RefState1(State next) {
			super(next);
		}

		@Override
		boolean isShift(Object o) {
			return true;
		}

		@Override
		State shift(Object o) {
			println("ref #" + o);

			return _next;
		}

		@Override
		State next(int ch) {
			return nextObject(ch);
		}
	}

	class DateState extends State {
		int _length;
		long _value;
		boolean _isMinute;

		DateState(State next) {
			super(next);
		}

		DateState(State next, boolean isMinute) {
			super(next);

			_length = 4;
			_isMinute = isMinute;
		}

		State next(int ch) {
			_value = 256 * _value + (ch & 0xff);

			if (++_length == 8) {
				java.util.Date value;

				if (_isMinute)
					value = new java.util.Date(_value * 60000L);
				else
					value = new java.util.Date(_value);

				if (_next.isShift(value))
					return _next.shift(value);
				else {
					printObject(value.toString());

					return _next;
				}
			} else
				return this;
		}
	}

	class DoubleState extends State {
		int _length;
		long _value;

		DoubleState(State next) {
			super(next);
		}

		State next(int ch) {
			_value = 256 * _value + (ch & 0xff);

			if (++_length == 8) {
				Double value = Double.longBitsToDouble(_value);

				if (_next.isShift(value))
					return _next.shift(value);
				else {
					printObject(value.toString());

					return _next;
				}
			} else
				return this;
		}
	}

	class MillsState extends State {
		int _length;
		int _value;

		MillsState(State next) {
			super(next);
		}

		State next(int ch) {
			_value = 256 * _value + (ch & 0xff);

			if (++_length == 4) {
				Double value = 0.001 * _value;

				if (_next.isShift(value))
					return _next.shift(value);
				else {
					printObject(value.toString());

					return _next;
				}
			} else
				return this;
		}
	}

	class StringState extends State {
		private static final int TOP = 0;
		private static final int UTF_2_1 = 1;
		private static final int UTF_3_1 = 2;
		private static final int UTF_3_2 = 3;

		char _typeCode;

		StringBuilder _value = new StringBuilder();
		int _lengthIndex;
		int _length;
		boolean _isLastChunk;

		int _utfState;
		char _ch;

		StringState(State next, char typeCode, boolean isLastChunk) {
			super(next);

			_typeCode = typeCode;
			_isLastChunk = isLastChunk;
		}

		StringState(State next, char typeCode, int length) {
			super(next);

			_typeCode = typeCode;
			_isLastChunk = true;
			_length = length;
			_lengthIndex = 2;
		}

		StringState(State next, char typeCode, int length, boolean isLastChunk) {
			super(next);

			_typeCode = typeCode;
			_isLastChunk = isLastChunk;
			_length = length;
			_lengthIndex = 1;
		}

		State next(int ch) {
			if (_lengthIndex < 2) {
				_length = 256 * _length + (ch & 0xff);

				if (++_lengthIndex == 2 && _length == 0 && _isLastChunk) {
					if (_next.isShift(_value.toString()))
						return _next.shift(_value.toString());
					else {
						printObject("\"" + _value + "\"");
						return _next;
					}
				} else
					return this;
			} else if (_length == 0) {
				if (ch == 's' || ch == 'x') {
					_isLastChunk = false;
					_lengthIndex = 0;
					return this;
				} else if (ch == 'S' || ch == 'X') {
					_isLastChunk = true;
					_lengthIndex = 0;
					return this;
				} else if (ch == 0x00) {
					if (_next.isShift(_value.toString()))
						return _next.shift(_value.toString());
					else {
						printObject("\"" + _value + "\"");
						return _next;
					}
				} else if (0x00 <= ch && ch < 0x20) {
					_isLastChunk = true;
					_lengthIndex = 2;
					_length = ch & 0xff;
					return this;
				} else if (0x30 <= ch && ch < 0x34) {
					_isLastChunk = true;
					_lengthIndex = 1;
					_length = (ch - 0x30);
					return this;
				} else {
					println(this + " " + String.valueOf((char) ch)
							+ ": unexpected character");
					return _next;
				}
			}

			switch (_utfState) {
			case TOP:
				if (ch < 0x80) {
					_length--;

					_value.append((char) ch);
				} else if (ch < 0xe0) {
					_ch = (char) ((ch & 0x1f) << 6);
					_utfState = UTF_2_1;
				} else {
					_ch = (char) ((ch & 0xf) << 12);
					_utfState = UTF_3_1;
				}
				break;

			case UTF_2_1:
			case UTF_3_2:
				_ch += ch & 0x3f;
				_value.append(_ch);
				_length--;
				_utfState = TOP;
				break;

			case UTF_3_1:
				_ch += (char) ((ch & 0x3f) << 6);
				_utfState = UTF_3_2;
				break;
			}

			if (_length == 0 && _isLastChunk) {
				if (_next.isShift(_value.toString()))
					return _next.shift(_value.toString());
				else {
					printObject("\"" + _value + "\"");

					return _next;
				}
			} else
				return this;
		}
	}

	class BinaryState extends State {
		char _typeCode;

		int _totalLength;

		int _lengthIndex;
		int _length;
		boolean _isLastChunk;

		BinaryState(State next, char typeCode, boolean isLastChunk) {
			super(next);

			_typeCode = typeCode;
			_isLastChunk = isLastChunk;
		}

		BinaryState(State next, char typeCode, int length) {
			super(next);

			_typeCode = typeCode;
			_isLastChunk = true;
			_length = length;
			_lengthIndex = 2;
		}

		BinaryState(State next, char typeCode, int length, boolean isLastChunk) {
			super(next);

			_typeCode = typeCode;
			_isLastChunk = isLastChunk;
			_length = length;
			_lengthIndex = 1;
		}

		@Override
		State next(int ch) {
			if (_lengthIndex < 2) {
				_length = 256 * _length + (ch & 0xff);

				if (++_lengthIndex == 2 && _length == 0 && _isLastChunk) {
					String value = "binary(" + _totalLength + ")";

					if (_next.isShift(value))
						return _next.shift(value);
					else {
						printObject(value);
						return _next;
					}
				} else
					return this;
			} else if (_length == 0) {
				if (ch == 'b' || ch == 'A') {
					_isLastChunk = false;
					_lengthIndex = 0;
					return this;
				} else if (ch == 'B') {
					_isLastChunk = true;
					_lengthIndex = 0;
					return this;
				} else if (ch == 0x20) {
					String value = "binary(" + _totalLength + ")";

					if (_next.isShift(value))
						return _next.shift(value);
					else {
						printObject(value);
						return _next;
					}
				} else if (0x20 <= ch && ch < 0x30) {
					_isLastChunk = true;
					_lengthIndex = 2;
					_length = (ch & 0xff) - 0x20;
					return this;
				} else {
					println(this + " 0x" + Integer.toHexString(ch) + " "
							+ String.valueOf((char) ch) + ": unexpected character");
					return _next;
				}
			}

			_length--;
			_totalLength++;

			if (_length == 0 && _isLastChunk) {
				String value = "binary(" + _totalLength + ")";

				if (_next.isShift(value))
					return _next.shift(value);
				else {
					printObject(value);

					return _next;
				}
			} else
				return this;
		}
	}

	class MapState extends State {
		private static final int TYPE = 0;
		private static final int KEY = 1;
		private static final int VALUE = 2;

		private int _refId;

		private int _state;
		private int _valueDepth;
		private boolean _hasData;

		MapState(State next, int refId) {
			super(next);

			_refId = refId;
			_state = TYPE;
		}

		MapState(State next, int refId, boolean isType) {
			super(next);

			_refId = refId;

			if (isType)
				_state = TYPE;
			else {
				printObject("map (#" + _refId + ")");
				_state = VALUE;
			}
		}

		@Override
		boolean isShift(Object value) {
			return _state == TYPE;
		}

		@Override
		State shift(Object type) {
			if (_state == TYPE) {
				if (type instanceof String) {
					_typeDefList.add((String) type);
				} else if (type instanceof Integer) {
					int iValue = (Integer) type;

					if (iValue >= 0 && iValue < _typeDefList.size())
						type = _typeDefList.get(iValue);
				}

				printObject("map " + type + " (#" + _refId + ")");

				_state = VALUE;

				return this;
			} else {
				printObject(this + " unknown shift state= " + _state + " type=" + type);

				return this;
			}
		}

		@Override
		int depth() {
			if (_state == TYPE)
				return _next.depth();
			else if (_state == KEY)
				return _next.depth() + 2;
			else
				return _valueDepth;
		}

		State next(int ch) {
			switch (_state) {
			case TYPE:
				return nextObject(ch);

			case VALUE:
				if (ch == 'Z') {
					if (_hasData)
						println();

					return _next;
				} else {
					if (_hasData)
						println();

					_hasData = true;
					_state = KEY;

					return nextObject(ch);
				}

			case KEY:
				print(" => ");
				_isObject = false;
				_valueDepth = _column;

				_state = VALUE;

				return nextObject(ch);

			default:
				throw new IllegalStateException();
			}
		}
	}

	class MapState1 extends State1 {
		private static final int TYPE = 0;
		private static final int KEY = 1;
		private static final int VALUE = 2;

		private int _refId;

		private int _state;
		private int _valueDepth;
		private boolean _hasData;

		MapState1(State next, int refId) {
			super(next);

			_refId = refId;
			_state = TYPE;
		}

		MapState1(State next, int refId, boolean isType) {
			super(next);

			_refId = refId;

			if (isType)
				_state = TYPE;
			else {
				printObject("map (#" + _refId + ")");
				_state = VALUE;
			}
		}

		@Override
		boolean isShift(Object value) {
			return _state == TYPE;
		}

		@Override
		State shift(Object type) {
			if (_state == TYPE) {
				if (type instanceof String) {
					_typeDefList.add((String) type);
				} else if (type instanceof Integer) {
					int iValue = (Integer) type;

					if (iValue >= 0 && iValue < _typeDefList.size())
						type = _typeDefList.get(iValue);
				}

				printObject("map " + type + " (#" + _refId + ")");

				_state = VALUE;

				return this;
			} else
				throw new IllegalStateException();
		}

		@Override
		int depth() {
			if (_state == TYPE)
				return _next.depth();
			else if (_state == KEY)
				return _next.depth() + 2;
			else
				return _valueDepth;
		}

		State next(int ch) {
			switch (_state) {
			case TYPE:
				if (ch == 't') {
					return new StringState(this, 't', true);
				} else if (ch == 'z') {
					println("map (#" + _refId + ")");
					return _next;
				} else {
					println("map (#" + _refId + ")");
					_hasData = true;
					_state = KEY;
					return nextObject(ch);
				}

			case VALUE:
				if (ch == 'z') {
					if (_hasData)
						println();

					return _next;
				} else {
					if (_hasData)
						println();

					_hasData = true;
					_state = KEY;

					return nextObject(ch);
				}

			case KEY:
				print(" => ");
				_isObject = false;
				_valueDepth = _column;

				_state = VALUE;

				return nextObject(ch);

			default:
				throw new IllegalStateException();
			}
		}
	}

	class ObjectDefState extends State {
		private static final int TYPE = 1;
		private static final int COUNT = 2;
		private static final int FIELD = 3;
		private static final int COMPLETE = 4;

		private int _state;
		private int _count;

		private String _type;
		private ArrayList<String> _fields = new ArrayList<String>();

		ObjectDefState(State next) {
			super(next);

			_state = TYPE;
		}

		@Override
		boolean isShift(Object value) {
			return true;
		}

		@Override
		State shift(Object object) {
			if (_state == TYPE) {
				_type = (String) object;

				print("/* defun " + _type + " [");

				_objectDefList.add(new ObjectDef(_type, _fields));

				_state = COUNT;
			} else if (_state == COUNT) {
				_count = (Integer) object;

				_state = FIELD;
			} else if (_state == FIELD) {
				String field = (String) object;

				_count--;

				_fields.add(field);

				if (_fields.size() == 1)
					print(field);
				else
					print(", " + field);
			} else {
				throw new UnsupportedOperationException();
			}

			return this;
		}

		@Override
		int depth() {
			if (_state <= TYPE)
				return _next.depth();
			else
				return _next.depth() + 2;
		}

		State next(int ch) {
			switch (_state) {
			case TYPE:
				return nextObject(ch);

			case COUNT:
				return nextObject(ch);

			case FIELD:
				if (_count == 0) {
					println("] */");
					_next.printIndent(0);

					return _next.nextObject(ch);
				} else
					return nextObject(ch);

			default:
				throw new IllegalStateException();
			}
		}
	}

	class ObjectState extends State {
		private static final int TYPE = 0;
		private static final int FIELD = 1;

		private int _refId;

		private int _state;
		private ObjectDef _def;
		private int _count;
		private int _fieldDepth;

		ObjectState(State next, int refId) {
			super(next);

			_refId = refId;
			_state = TYPE;
		}

		ObjectState(State next, int refId, int def) {
			super(next);

			_refId = refId;
			_state = FIELD;

			if (def < 0 || _objectDefList.size() <= def) {
				log.warning(this + " " + def + " is an unknown object type");

				println(this + " object unknown  (#" + _refId + ")");
			}

			_def = _objectDefList.get(def);

			if (_isObject)
				println();

			println("object " + _def.getType() + " (#" + _refId + ")");
		}

		@Override
		boolean isShift(Object value) {
			if (_state == TYPE)
				return true;
			else
				return false;
		}

		@Override
		State shift(Object object) {
			if (_state == TYPE) {
				int def = (Integer) object;

				_def = _objectDefList.get(def);

				println("object " + _def.getType() + " (#" + _refId + ")");

				_state = FIELD;

				if (_def.getFields().size() == 0)
					return _next;
			}

			return this;
		}

		@Override
		int depth() {
			if (_state <= TYPE)
				return _next.depth();
			else
				return _fieldDepth;
		}

		State next(int ch) {
			switch (_state) {
			case TYPE:
				return nextObject(ch);

			case FIELD:
				if (_def.getFields().size() <= _count)
					return _next.next(ch);

				_fieldDepth = _next.depth() + 2;
				println();
				print(_def.getFields().get(_count++) + ": ");

				_fieldDepth = _column;

				_isObject = false;
				return nextObject(ch);

			default:
				throw new IllegalStateException();
			}
		}
	}

	class ListState1 extends State1 {
		private static final int TYPE = 0;
		private static final int LENGTH = 1;
		private static final int VALUE = 2;

		private int _refId;

		private int _state;
		private int _count;
		private int _valueDepth;

		ListState1(State next, int refId) {
			super(next);

			_refId = refId;

			_state = TYPE;
		}

		@Override
		boolean isShift(Object value) {
			return _state == TYPE || _state == LENGTH;
		}

		@Override
		State shift(Object object) {
			if (_state == TYPE) {
				Object type = object;

				if (type instanceof String) {
					_typeDefList.add((String) type);
				} else if (object instanceof Integer) {
					int index = (Integer) object;

					if (index >= 0 && index < _typeDefList.size())
						type = _typeDefList.get(index);
					else
						type = "type-unknown(" + index + ")";
				}

				printObject("list " + type + "(#" + _refId + ")");

				_state = VALUE;

				return this;
			} else if (_state == LENGTH) {
				_state = VALUE;

				return this;
			} else
				return this;
		}

		@Override
		int depth() {
			if (_state <= LENGTH)
				return _next.depth();
			else if (_state == VALUE)
				return _valueDepth;
			else
				return _next.depth() + 2;
		}

		State next(int ch) {
			switch (_state) {
			case TYPE:
				if (ch == 'z') {
					printObject("list (#" + _refId + ")");

					return _next;
				} else if (ch == 't') {
					return new StringState(this, 't', true);
				} else {
					printObject("list (#" + _refId + ")");
					printObject("  " + _count++ + ": ");
					_valueDepth = _column;
					_isObject = false;
					_state = VALUE;

					return nextObject(ch);
				}

			case VALUE:
				if (ch == 'z') {
					if (_count > 0)
						println();

					return _next;
				} else {
					_valueDepth = _next.depth() + 2;
					println();
					printObject(_count++ + ": ");
					_valueDepth = _column;
					_isObject = false;

					return nextObject(ch);
				}

			default:
				throw new IllegalStateException();
			}
		}
	}

	class ListState extends State {
		private static final int TYPE = 0;
		private static final int LENGTH = 1;
		private static final int VALUE = 2;

		private int _refId;

		private int _state;
		private int _count;
		private int _valueDepth;

		ListState(State next, int refId, boolean isType) {
			super(next);

			_refId = refId;

			if (isType)
				_state = TYPE;
			else {
				printObject("list (#" + _refId + ")");
				_state = VALUE;
			}
		}

		@Override
		boolean isShift(Object value) {
			return _state == TYPE || _state == LENGTH;
		}

		@Override
		State shift(Object object) {
			if (_state == TYPE) {
				Object type = object;

				if (type instanceof String) {
					_typeDefList.add((String) type);
				} else if (object instanceof Integer) {
					int index = (Integer) object;

					if (index >= 0 && index < _typeDefList.size())
						type = _typeDefList.get(index);
					else
						type = "type-unknown(" + index + ")";
				}

				printObject("list " + type + "(#" + _refId + ")");

				_state = VALUE;

				return this;
			} else if (_state == LENGTH) {
				_state = VALUE;

				return this;
			} else
				return this;
		}

		@Override
		int depth() {
			if (_state <= LENGTH)
				return _next.depth();
			else if (_state == VALUE)
				return _valueDepth;
			else
				return _next.depth() + 2;
		}

		State next(int ch) {
			switch (_state) {
			case TYPE:
				return nextObject(ch);

			case VALUE:
				if (ch == 'Z') {
					if (_count > 0)
						println();

					return _next;
				} else {
					_valueDepth = _next.depth() + 2;
					println();
					printObject(_count++ + ": ");
					_valueDepth = _column;
					_isObject = false;

					return nextObject(ch);
				}

			default:
				throw new IllegalStateException();
			}
		}
	}

	class CompactListState extends State {
		private static final int TYPE = 0;
		private static final int LENGTH = 1;
		private static final int VALUE = 2;

		private int _refId;

		private boolean _isTyped;
		private boolean _isLength;

		private int _state;
		private int _length;
		private int _count;
		private int _valueDepth;

		CompactListState(State next, int refId, boolean isTyped) {
			super(next);

			_isTyped = isTyped;
			_refId = refId;

			if (isTyped)
				_state = TYPE;
			else
				_state = LENGTH;
		}

		CompactListState(State next, int refId, boolean isTyped, int length) {
			super(next);

			_isTyped = isTyped;
			_refId = refId;
			_length = length;

			_isLength = true;

			if (isTyped)
				_state = TYPE;
			else {
				printObject("list (#" + _refId + ")");

				_state = VALUE;
			}
		}

		@Override
		boolean isShift(Object value) {
			return _state == TYPE || _state == LENGTH;
		}

		@Override
		State shift(Object object) {
			if (_state == TYPE) {
				Object type = object;

				if (object instanceof Integer) {
					int index = (Integer) object;

					if (index >= 0 && index < _typeDefList.size())
						type = _typeDefList.get(index);
					else
						type = "type-unknown(" + index + ")";
				} else if (object instanceof String)
					_typeDefList.add((String) object);

				printObject("list " + type + " (#" + _refId + ")");

				if (_isLength) {
					_state = VALUE;

					if (_length == 0)
						return _next;
				} else
					_state = LENGTH;

				return this;
			} else if (_state == LENGTH) {
				_length = (Integer) object;

				if (!_isTyped)
					printObject("list (#" + _refId + ")");

				_state = VALUE;

				if (_length == 0)
					return _next;
				else
					return this;
			} else
				return this;
		}

		@Override
		int depth() {
			if (_state <= LENGTH)
				return _next.depth();
			else if (_state == VALUE)
				return _valueDepth;
			else
				return _next.depth() + 2;
		}

		State next(int ch) {
			switch (_state) {
			case TYPE:
				return nextObject(ch);

			case LENGTH:
				return nextObject(ch);

			case VALUE:
				if (_length <= _count)
					return _next.next(ch);
				else {
					_valueDepth = _next.depth() + 2;
					println();
					printObject(_count++ + ": ");
					_valueDepth = _column;
					_isObject = false;

					return nextObject(ch);
				}

			default:
				throw new IllegalStateException();
			}
		}
	}

	class Hessian2State extends State {
		private static final int MAJOR = 0;
		private static final int MINOR = 1;

		private int _state;
		private int _major;
		private int _minor;

		Hessian2State(State next) {
			super(next);
		}

		int depth() {
			return _next.depth() + 2;
		}

		State next(int ch) {
			switch (_state) {
			case MAJOR:
				_major = ch;
				_state = MINOR;
				return this;

			case MINOR:
				_minor = ch;
				println(-2, "Hessian " + _major + "." + _minor);
				return _next;

			default:
				throw new IllegalStateException();
			}
		}
	}

	class CallState1 extends State1 {
		private static final int MAJOR = 0;
		private static final int MINOR = 1;
		private static final int HEADER = 2;
		private static final int METHOD = 3;
		private static final int VALUE = 4;
		private static final int ARG = 5;

		private int _state;
		private int _major;
		private int _minor;

		CallState1(State next) {
			super(next);
		}

		int depth() {
			return _next.depth() + 2;
		}

		State next(int ch) {
			switch (_state) {
			case MAJOR:
				_major = ch;
				_state = MINOR;
				return this;

			case MINOR:
				_minor = ch;
				_state = HEADER;
				println(-2, "call " + _major + "." + _minor);
				return this;

			case HEADER:
				if (ch == 'H') {
					println();
					print("header ");
					_isObject = false;
					_state = VALUE;
					return new StringState(this, 'H', true);
				} else if (ch == 'm') {
					println();
					print("method ");
					_isObject = false;
					_state = ARG;
					return new StringState(this, 'm', true);
				} else {
					println((char) ch + ": unexpected char");
					return popStack();
				}

			case VALUE:
				print(" => ");
				_isObject = false;
				_state = HEADER;
				return nextObject(ch);

			case ARG:
				if (ch == 'z') {
					println();
					return _next;
				} else
					return nextObject(ch);

			default:
				throw new IllegalStateException();
			}
		}
	}

	class Call2State extends State {
		private static final int METHOD = 0;
		private static final int COUNT = 1;
		private static final int ARG = 2;

		private int _state = METHOD;
		private int _i;
		private int _count;

		Call2State(State next) {
			super(next);
		}

		int depth() {
			return _next.depth() + 5;
		}

		@Override
		boolean isShift(Object value) {
			return _state != ARG;
		}

		@Override
		State shift(Object object) {
			if (_state == METHOD) {
				println(-5, "Call " + object);

				_state = COUNT;
				return this;
			} else if (_state == COUNT) {
				Integer count = (Integer) object;

				_count = count;

				_state = ARG;

				if (_count == 0) {
					return _next;
				} else
					return this;
			} else {
				return this;
			}
		}

		@Override
		State next(int ch) {
			switch (_state) {
			case COUNT:
				return nextObject(ch);

			case METHOD:
				return nextObject(ch);

			case ARG:
				if (_count <= _i) {
					println();
					return _next.next(ch);
				} else {
					println();
					print(-3, _i++ + ": ");

					return nextObject(ch);
				}

			default:
				throw new IllegalStateException();
			}
		}
	}

	class ReplyState1 extends State1 {
		private static final int MAJOR = 0;
		private static final int MINOR = 1;
		private static final int HEADER = 2;
		private static final int VALUE = 3;
		private static final int END = 4;

		private int _state;
		private int _major;
		private int _minor;

		ReplyState1(State next) {
			_next = next;
		}

		int depth() {
			return _next.depth() + 2;
		}

		State next(int ch) {
			switch (_state) {
			case MAJOR:
				if (ch == 't' || ch == 'S')
					return new RemoteState(this).next(ch);

				_major = ch;
				_state = MINOR;
				return this;

			case MINOR:
				_minor = ch;
				_state = HEADER;
				println(-2, "reply " + _major + "." + _minor);
				return this;

			case HEADER:
				if (ch == 'H') {
					_state = VALUE;
					return new StringState(this, 'H', true);
				} else if (ch == 'f') {
					print("fault ");
					_isObject = false;
					_state = END;
					return new MapState(this, 0);
				} else {
					_state = END;
					return nextObject(ch);
				}

			case VALUE:
				_state = HEADER;
				return nextObject(ch);

			case END:
				println();
				if (ch == 'z') {
					return _next;
				} else
					return _next.next(ch);

			default:
				throw new IllegalStateException();
			}
		}
	}

	class Reply2State extends State {
		Reply2State(State next) {
			super(next);

			println(-2, "Reply");
		}

		int depth() {
			return _next.depth() + 2;
		}

		@Override
		State next(int ch) {
			if (ch < 0) {
				println();
				return _next;
			} else {
				return nextObject(ch);
			}
		}
	}

	class Fault2State extends State {
		Fault2State(State next) {
			super(next);

			println(-2, "Fault");
		}

		int depth() {
			return _next.depth() + 2;
		}

		@Override
		State next(int ch) {
			return nextObject(ch);
		}
	}

	class IndirectState extends State {
		IndirectState(State next) {
			super(next);
		}

		boolean isShift(Object object) {
			return _next.isShift(object);
		}

		State shift(Object object) {
			return _next.shift(object);
		}

		State next(int ch) {
			return nextObject(ch);
		}
	}

	class RemoteState extends State {
		private static final int TYPE = 0;
		private static final int VALUE = 1;
		private static final int END = 2;

		private int _state;
		private int _major;
		private int _minor;

		RemoteState(State next) {
			super(next);
		}

		@Override
		State next(int ch) {
			switch (_state) {
			case TYPE:
				println(-1, "remote");
				if (ch == 't') {
					_state = VALUE;
					return new StringState(this, 't', false);
				} else {
					_state = END;
					return nextObject(ch);
				}

			case VALUE:
				_state = END;
				return _next.nextObject(ch);

			case END:
				return _next.next(ch);

			default:
				throw new IllegalStateException();
			}
		}
	}

	class StreamingState extends State {
		private long _length;
		private int _metaLength;
		private boolean _isLast;
		private boolean _isFirst = true;

		private boolean _isLengthState;

		private State _childState;

		StreamingState(State next, boolean isLast) {
			super(next);

			_isLast = isLast;
			_childState = new InitialState();
		}

		State next(int ch) {
			if (_metaLength > 0) {
				_length = 256 * _length + ch;
				_metaLength--;

				if (_metaLength == 0 && _isFirst) {
					if (_isLast)
						println(-1, "--- packet-start(" + _length + ")");
					else
						println(-1, "--- packet-start(fragment)");
					_isFirst = false;
				}

				return this;
			}

			if (_length > 0) {
				_length--;
				_childState = _childState.next(ch);

				return this;
			}

			if (!_isLengthState) {
				_isLengthState = true;

				if (_isLast) {
					println(-1, "");
					println(-1, "--- packet-end");
					_refId = 0;

					_isFirst = true;
				}

				_isLast = (ch & 0x80) == 0x00;
				_isLengthState = true;
			} else {
				_isLengthState = false;
				_length = (ch & 0x7f);

				if (_length == 0x7e) {
					_length = 0;
					_metaLength = 2;
				} else if (_length == 0x7f) {
					_length = 0;
					_metaLength = 8;
				} else {
					if (_isFirst) {
						if (_isLast)
							println(-1, "--- packet-start(" + _length + ")");
						else
							println(-1, "--- packet-start(fragment)");
						_isFirst = false;
					}
				}
			}

			return this;
		}
	}

	static class ObjectDef {
		private String _type;
		private ArrayList<String> _fields;

		ObjectDef(String type, ArrayList<String> fields) {
			_type = type;
			_fields = fields;
		}

		String getType() {
			return _type;
		}

		ArrayList<String> getFields() {
			return _fields;
		}
	}
}
